#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "dgx.tab.h"

void recursive(FILE* stream, struct ast* root, int layer);

typedef enum {
    UNDEF, P_L, P_LP, L_SS, S_IEE, S_ITE, S_WD, S_IT, S_LPR, C_EGE, C_ELE,
    C_EEE, E_EAT, E_EST, E_T, T_F, T_TMF, T_TDF, F_LER, F_I, F_I8,
    F_I16, F_I10, F_F, I_I
} ast_type;

struct ast
{
    ast_type nodetype;
    struct ast *left;
    struct ast *middle;
    struct ast *right;
    union 
    {
        int ival;
        float fval;
        char *name;
    } val;
    
};

struct ast *newast(int nodetype, struct ast *left, struct ast *middle, struct ast *right)
{
    struct ast *a = malloc(sizeof(struct ast));
    if (!a)
    {
        yyerror("oom");
        exit(0);
    }
    a->nodetype = nodetype;
    a->left = left;
    a->middle = middle;
    a->right = right;
    return a;
}

struct ast *newfint(int nodetype, int ival)
{
    struct ast *a = newast(nodetype, NULL, NULL, NULL);
    a->val.ival = ival;
    return a;
}

struct ast *newffloat(int nodetype, float fval)
{
    struct ast *a = newast(nodetype, NULL, NULL, NULL);
    a->val.fval = fval;
    return a;
}

struct ast *newident(int nodetype, char *name)
{
    struct ast *a = newast(nodetype, NULL, NULL, NULL);
    a->val.name = strdup(name);
    // printf("!!!!!!!!!%s %s\n", name, a->val.name);
    return a;
}

void printast(FILE* stream, struct ast* root)
{
    if (!stream) {
        fprintf(stderr, "Error: Unable to use stream. Use stdout instead.\n");
        stream = stdout;
    }
    recursive(stream, root, 0);
}

char* looktype(ast_type type) {
    switch (type)
    {
        case P_L:       return "P -> L";
        case P_LP:      return "P -> L P";
        case L_SS:      return "L -> S;";
        case S_IEE:     return "S -> id = E";
        case S_ITE:     return "S -> if C then S else S";
        case S_WD:      return "S -> while C do S";
        case S_IT:      return "S -> if C then S";
        case S_LPR:     return "S -> { P }";
        case C_EGE:     return "C -> E > E";
        case C_ELE:     return "C -> E < E";
        case C_EEE:     return "C -> E = E";
        case E_EAT:     return "E -> E + T";
        case E_EST:     return "E -> E - T";
        case E_T:       return "E -> T";
        case T_F:       return "T -> F";
        case T_TMF:     return "T -> T * F";
        case T_TDF:     return "T -> T / F";
        case F_LER:     return "F -> ( E )";
        case F_I:       return "F -> I";
        case F_I8:      return "F -> Integer 8";
        case F_I16:     return "F -> Integer 16";
        case F_I10:     return "F -> Integer 10";
        case F_F:       return "F -> Float";
        case I_I:       return "I -> id";
        default:        return "UNDEFINED";
    }
}

void recursive(FILE* stream, struct ast* root, int layer) {
    if (!root)  return;
    for (int i = 0; i < layer; i++) {
        fprintf(stream, " ");
    }
    char* name = looktype(root->nodetype);
    fprintf(stream, "%s\n", name);
    switch (root->nodetype) {
        case F_I8:
        case F_I10:
        case F_I16: for (int i = 0; i < layer + 1; i++) {
                        fprintf(stream, " ");
                    }
                    fprintf(stream, "%d\n", root->val.ival);
                    break;
        case F_F: for (int i = 0; i < layer + 1; i++) {
                        fprintf(stream, " ");
                    }
                    fprintf(stream, "%lf\n", root->val.fval);
                    break;
        case I_I:   for (int i = 0; i < layer + 1; i++) {
                        fprintf(stream, " ");
                    }
                    fprintf(stream, "%s\n", root->val.name);
                    break;
        default:    break;
    }
    recursive(stream, root->left, layer + 1);
    recursive(stream, root->middle, layer + 1);
    recursive(stream, root->right, layer + 1);
}
